/* Copyright ©, 2022, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary to ECARX
 * (Hubei) Technology Co., Ltd. and/or other developers. No license to any intellectual
 * property rights contained within this file is either granted or implied except as may be
 * provided in an agreement with ECARX (Hubei) Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole or in part
 * is prohibited.
 */
#define LOG_NDEBUG 0
#define LOG_TAG "VHalProperties"
#include "vhal_core/persist/Properties.h"

#include "CarProperties.h"
#include <PropertiesParser.h>
#include <sys/stat.h>

#include <fstream>
#include <iterator>
#include <sstream>
#include <string>

#include "VHalLog.h"
#include "vhal_core/persist/Properties.h"

namespace ecarx::vhal::core::impl::looper {
extern android::sp<android::Looper> mHandler;
}

namespace Looper = ecarx::vhal::core::impl::looper;

#define SAVE_MSG 10001
#define SAVE_MSG_DELAY 2000
namespace ecarx::vhal::core::persist {

static void ifNoExistCreate(const char* path) {
    struct stat buffer;
    if ((stat(path, &buffer) != 0)) {
        std::ofstream fout(path);
        if (fout) {
            fout.close();
        }
        VHALOG.d("create file %s", path);
    } else {
        VHALOG.d("exist file %s", path);
    }
}

static std::string filePath(const char* path, const char* name) {
    std::string filePath(path);
    if (filePath.find_last_not_of('/') == (filePath.size() - 1)) {
        filePath.append("/");
    }
    filePath.append(name);
    return filePath;
}

struct PersistSaveMessageHanlder : public android::MessageHandler {
    std::function<void()> saveFunction;
    PersistSaveMessageHanlder(std::function<void()>&& fn) : saveFunction(std::move(fn)) {}
    void handleMessage(const android::Message& message) override {
        if (message.what == SAVE_MSG) {
            saveFunction();
        }
    }
};

class Properties::Persist : public ::vhal::CarProperties {
  public:
    bool hasProperty(const std::string& key) { return mProperties.hasProperty(key); }
    void load(const char* path) {
        // load properties
        mProperties = cppproperties::PropertiesParser::Read(path);
    }
    void save(const char* path) { cppproperties::PropertiesParser::Write(path, mProperties); }
};

Properties::Properties(const char* path, const char* name)
    : mPersist(std::make_unique<Persist>()),
      mFilePath(filePath(path, name)),
      changed(false),
      isDeleted(false),
      mMessageHandler(new PersistSaveMessageHanlder([this]() { save(); })) {
    ifNoExistCreate(mFilePath.c_str());
    mPersist->load(mFilePath.c_str());
}
Properties::~Properties() {
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
}

void Properties::save() {
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if (changed) {
        VHALOG.d("save properties %s", mFilePath.c_str());
        mPersist->save(mFilePath.c_str());
        changed = false;
    }
}

bool Properties::deleteFile(){
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    isDeleted = remove(mFilePath.c_str()) == 0;
    if(!isDeleted){
        Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
    }
    return isDeleted;
}

const std::string& Properties::getFilePath() const {
    return mFilePath;
}

std::string Properties::getString(const std::string& key, const std::string& def) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    return mPersist->GetProperty(key, def);
}

void Properties::setString(const std::string& key, const std::string& value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    mPersist->SetProperty(key, value);
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

int32_t Properties::getInt32(const std::string& key, int32_t def) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    return mPersist->GetIntProperty(key, def);
}

void Properties::setInt32(const std::string& key, int32_t value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    mPersist->SetIntProperty(key, value);
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

std::vector<int32_t> Properties::getInt32Array(const std::string& key,
                                               const std::vector<int32_t>& defaultValue) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    std::istringstream ss(mPersist->GetProperty(key, ""));
    std::string token;
    std::vector<int32_t> value;
    while (std::getline(ss, token, ',')) {
        value.emplace_back(std::stoi(token));
    }
    return value.empty() ? defaultValue : value;
}

void Properties::setInt32Array(const std::string& key, const std::vector<int32_t>& value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    std::stringstream ss;
    std::copy(value.begin(), value.end(), std::ostream_iterator<int32_t>(ss, ","));
    mPersist->SetProperty(key, ss.str());
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

int64_t Properties::getInt64(const std::string& key, int64_t def) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    return mPersist->GetIntProperty(key, def);
}

void Properties::setInt64(const std::string& key, int64_t value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    mPersist->SetIntProperty(key, value);
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

std::vector<int64_t> Properties::getInt64Array(const std::string& key,
                                               const std::vector<int64_t>& defaultValue) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    std::istringstream ss(mPersist->GetProperty(key, ""));
    std::string token;
    std::vector<int64_t> value;
    while (std::getline(ss, token, ',')) {
        value.emplace_back(std::stol(token));
    }
    return value.empty() ? defaultValue : value;
}

void Properties::setInt64Array(const std::string& key, const std::vector<int64_t>& value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    std::stringstream ss;
    std::copy(value.begin(), value.end(), std::ostream_iterator<int64_t>(ss, ","));
    mPersist->SetProperty(key, ss.str());
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

float Properties::getFloat(const std::string& key, const float def) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    return static_cast<float>(mPersist->GetDoubleProperty(key, def));
}

void Properties::setFloat(const std::string& key, const float value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    mPersist->SetDoubleProperty(key, value);
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

std::vector<float> Properties::getFloatArray(const std::string& key,
                                             const std::vector<float>& defaultValue) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    std::istringstream ss(mPersist->GetProperty(key, ""));
    std::string token;
    std::vector<float> value;
    while (std::getline(ss, token, ',')) {
        value.emplace_back(std::stof(token));
    }
    return value.empty() ? defaultValue : value;
}

void Properties::setFloatArray(const std::string& key, const std::vector<float>& value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    std::stringstream ss;
    std::copy(value.begin(), value.end(), std::ostream_iterator<float>(ss, ","));
    mPersist->SetProperty(key, ss.str());
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

std::vector<uint8_t> Properties::getBytes(const std::string& key, const std::vector<uint8_t>& def) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    std::istringstream ss(mPersist->GetProperty(key, ""));
    std::string token;
    std::vector<uint8_t> value;
    while (std::getline(ss, token, ',')) {
        value.emplace_back(std::stof(token));
    }
    return value.empty() ? def : value;
}

void Properties::setBytes(const std::string& key, const std::vector<uint8_t>& value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    std::stringstream ss;
    std::copy(value.begin(), value.end(), std::ostream_iterator<uint8_t>(ss, ","));
    mPersist->SetProperty(key, ss.str());
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

bool Properties::getBool(const std::string& key, const bool def) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    return mPersist->GetBoolProperty(key, def);
}

void Properties::setBool(const std::string& key, const bool value) {
    std::unique_lock<std::shared_mutex> lock(mRWMutex);
    if(isDeleted){
        return;
    }
    Looper::mHandler->removeMessages(mMessageHandler, SAVE_MSG);
    mPersist->SetBoolProperty(key, value);
    changed = true;
    Looper::mHandler->sendMessageDelayed(milliseconds_to_nanoseconds(SAVE_MSG_DELAY), mMessageHandler,
                                         android::Message(SAVE_MSG));
}

bool Properties::hasProperty(const std::string& key) {
    std::shared_lock<std::shared_mutex> lock(mRWMutex);
    return mPersist->hasProperty(key);
}

}  // namespace ecarx::vhal::core::persist
